package com.young.springboot.httpclient;

import com.young.commom.httpclient.HttpClientConfiguration;
import com.young.commom.httpclient.HttpClientTemplate;
import com.young.commom.httpclient.components.HttpComponentsClientBuilder;
import com.young.commom.httpclient.components.HttpComponentsClientTemplate;
import com.young.commom.httpclient.okhttp.OkHttpClientBuilder;
import com.young.commom.httpclient.okhttp.OkHttpClientTemplate;
import com.young.common.context.Context;
import com.young.common.context.ContextConstants;
import com.young.common.context.HttpConstants;
import com.young.common.core.net.InetTool;
import com.young.common.expression.dns.CommonResolver;
import com.young.common.expression.dns.NamingResolver;
import com.young.springboot.context.ContextAutoConfiguration;
import com.young.springboot.context.ContextProperties;
import okhttp3.OkHttpClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * @description：自动装配 HttpClient
 * @author: yangyahui01
 * @date: 11/29/24 2:39 PM
 */
@Configuration
@ConditionalOnWebApplication
@ConditionalOnBean(Context.class)
@AutoConfigureAfter({ContextAutoConfiguration.class})
@EnableConfigurationProperties({ContextProperties.class, HttpClientProperties.class})
public class HttpClientAutoConfiguration {

    private final ContextProperties contextProperties;
    private final HttpClientProperties httpClientProperties;

    private final Context context;

    protected static final Log LOGGER = LogFactory.getLog(HttpClientAutoConfiguration.class);

    public HttpClientAutoConfiguration(ContextProperties contextProperties,
                                       HttpClientProperties httpClientProperties,
                                       ObjectProvider<Context> contextProvider) {
        this.contextProperties = contextProperties;
        this.httpClientProperties = httpClientProperties;
        this.context = contextProvider.getIfUnique();
    }

    @Bean
    @ConditionalOnMissingBean
    public HttpClientConfiguration httpClientConfiguration() {
        HttpClientConfiguration configuration = HttpClientConfiguration.common();
        String clientType = httpClientProperties.getType();
        if (clientType != null) {
            configuration.setClientType(clientType);
        }

        String charset = httpClientProperties.getCharset();
        if (charset == null) {
            charset = contextProperties.getCharset();
        }
        configuration.setCharset(charset == null ? ContextConstants.DEFAULT_CHARSET : charset);
        Long connectionTimeout = httpClientProperties.getConnectionTimeout().toMillis();
        if (connectionTimeout != null) {
            configuration.setConnectionTimeout(connectionTimeout);
        }
        Long keepAliveTime = httpClientProperties.getKeepAliveTime().toMillis();
        if (keepAliveTime != null) {
            configuration.setKeepAliveTime(keepAliveTime);
        }
        Boolean retryOnFailure = httpClientProperties.getRetryOnFailure();
        if (retryOnFailure != null) {
            configuration.setRetryOnFailure(retryOnFailure);
        }
        Integer retryTimes = httpClientProperties.getRetryTimes();
        if (retryTimes != null) {
            configuration.setRetryTimes(retryTimes);
        }
        Long socketTimeout = httpClientProperties.getSocketTimeout().toMillis();
        if (socketTimeout != null) {
            configuration.setSocketTimeout(socketTimeout);
        }
        Long requestTimeout = httpClientProperties.getRequestTimeout().toMillis();
        if (requestTimeout != null) {
            configuration.setRequestTimeout(requestTimeout);
        }
        //全局header，用户通过yml配置的优先级最低
        Map<String, String> globalHeaders = httpClientProperties.getGlobalHeaders();
        if (globalHeaders == null) {
            globalHeaders = new HashMap<>();
        }
        if (context.getProject() != null) {
            globalHeaders.put(HttpConstants.HTTP_HEADER_PROJECT, context.getProject());
        }
        if (context.getCloud() != null) {
            globalHeaders.put(HttpConstants.HTTP_HEADER_CLOUD, context.getCloud());
        }
        if (context.getGroup() != null) {
            globalHeaders.put(HttpConstants.HTTP_HEADER_GROUP, context.getGroup());
        }

        /** set the dns properties of {@link HttpClientConfiguration} according to dns-resolver's config */
        configuration.setDnsResolverEnabled(httpClientProperties.getDnsResolver().isEnabled());
        if (httpClientProperties.getDnsResolver().isEnabled()) {
            configuration.setDnsResolverEnabled(true);
            configuration.setNamingResolver(dnsResolver());
        }
        globalHeaders.put(HttpConstants.HTTP_HEADER_ADDRESS, InetTool.LOCAL_IP);

        configuration.setGlobalHeaders(globalHeaders);
        return configuration;
    }

    @Bean
    @ConditionalOnMissingBean
    public MeteorRestTemplateCustomizer meteorRestTemplateCustomizer(HttpClientConfiguration httpClientConfiguration) {
        return new MeteorRestTemplateCustomizer(httpClientConfiguration);
    }

    @Bean(name = "httpClientTemplate", initMethod = "init", destroyMethod = "close")
    @ConditionalOnMissingBean
    public HttpClientTemplate meteorHttpClientTemplate(HttpClientConfiguration httpClientConfiguration) {
        return build(httpClientConfiguration);
    }


    @Bean
    @ConditionalOnProperty(name = "spring.http-client.dns-resolver.enabled",havingValue = "true")
    public NamingResolver dnsResolver() {
        HttpClientProperties.DnsResolver dnsResolver = httpClientProperties.getDnsResolver();
        CommonResolver resolver = new CommonResolver();
        resolver.addDomainResolvers(dnsResolver.getDomain());
        resolver.addServiceResolvers(dnsResolver.getService());
        return resolver;
    }

    private HttpClientTemplate build(HttpClientConfiguration configuration) {
        String clientType = configuration.getClientType();
        if (HttpConstants.HTTP_CLIENT_TYPE_OK_HTTP.equalsIgnoreCase(clientType)) {
            return new OkHttpClientTemplate(configuration);
        } else {
            return new HttpComponentsClientTemplate(configuration);
        }
    }

    private static ClientHttpRequestFactory clientHttpRequestFactory(HttpClientConfiguration configuration) {
        String clientType = configuration.getClientType();

        if (HttpConstants.HTTP_CLIENT_TYPE_OK_HTTP.equalsIgnoreCase(clientType)) {
            OkHttpClient httpClient = OkHttpClientBuilder.build(configuration);
            return new OkHttp3ClientHttpRequestFactory(httpClient);
        }
        HttpClient httpClient = HttpComponentsClientBuilder.build(configuration);
        return new HttpComponentsClientHttpRequestFactory(httpClient);
    }



    public static class MeteorRestTemplateCustomizer implements RestTemplateCustomizer {

        private final HttpClientConfiguration configuration;

        public MeteorRestTemplateCustomizer(HttpClientConfiguration configuration) {
            this.configuration = configuration;
        }

        @Override
        public void customize(RestTemplate restTemplate) {
            restTemplate.setRequestFactory(clientHttpRequestFactory(configuration));
            MeteorHttpClientRequestHeaderInterceptor interceptor = new MeteorHttpClientRequestHeaderInterceptor();
            interceptor.setDnsResolver(configuration.getNamingResolver());
            interceptor.setGlobalHeaders(configuration.getGlobalHeaders());
            restTemplate.getInterceptors().add(interceptor);
            LOGGER.info("框架已为'RestTemplate'添加默认拦截器:[MeteorHttpClientRequestHeaderInterceptor]");
        }
    }
}
